package com.xisoft.base.service;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xisoft.base.entity.BaseVO;
import com.xisoft.base.entity.IBaseVO;
import com.xisoft.base.entity.ReferEntity;
import com.xisoft.base.mapper.XfBaseMapper;
import com.xisoft.base.page.Query;
import com.xisoft.common.*;
import com.xisoft.common.db.SqlHelper;
import com.xisoft.common.bean.BeanHelper;
import com.xisoft.common.bean.ReflectionUtil;
import com.xisoft.common.pubEnv.CollectionUtil;
import com.xisoft.common.pubEnv.JsonUtil;
import com.xisoft.common.pubEnv.PubEnvUtil;
import com.xisoft.common.pubEnv.StringUtil;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created with Description:
 *
 * @version 1.0 <br>
 * @packagename: com.xisoft.abs<br>
 * @author: LiBencheng <br>
 * @date: Created on 2022/7/19 10:09 <br>
 * @computer: licheng <br>
 */
@Service ("XfServiceImpl")
public class XfServiceImpl< MAP extends XfBaseMapper< T >, T extends BaseVO >
        extends ServiceImpl< MAP,T > implements XfService< T >, Serializable
{
    public static long serialVersionUID = 1L;

    private XiBeanHelper bhelp = null;

    public XiBeanHelper getBhelp( )
    {
        if (null == bhelp)
        {
            bhelp = new XiBeanHelper();
        }

        return bhelp;
    }

    @Getter
    @Autowired
    private XfBaseMapper xbaseMapper;

    @Override public Map< String,Object > getField(String table, String field)
    { return getXbaseMapper().getField(table, field); }

    public int deleteData(String json) throws Exception
    {
        Map< String,Object > toMap = JsonUtil.toMap(json);
        BaseVO baseEntity = ReflectionUtil.newInstance(StringUtil.toString(toMap.get("classpatch")));
        List< String > ids = Arrays.asList(StringUtil.toString(toMap.get("id")).split(PubEnvUtil.COMMA));
        String selectSQL = SqlHelper.getSelectSQL(baseEntity.getTableName()
                , new String[]{baseEntity.getPrimaryKey()}
                , SqlHelper.getPkInSQLByCol(baseEntity.getPrimaryKey(), ids), null);
        System.out.println(selectSQL);

        Map< String,String > childMap = baseEntity.getChildEntity();
        for (String tableCode : childMap.keySet())
        {
            BaseVO childEntity = ReflectionUtil.newInstance(childMap.get(tableCode));
            System.out.println(childEntity.getChildEntity());
            String deleteSQL = SqlHelper.getDeleteSQL(childEntity.getTableName()
                    , baseEntity.getPrimaryKey() + String.format(SqlHelper.in, selectSQL));
            System.out.println(deleteSQL);
        }

        String deleteSQL = SqlHelper.getDeleteSQL(baseEntity.getTableName(),
                                                  SqlHelper.getPkInSQLByCol(baseEntity.getPrimaryKey(), ids));
        System.out.println(deleteSQL);

        return 0;
    }

    public int insertOrUpdata(List< BaseVO > baseList) throws Exception
    {
        List insert = new CopyOnWriteArrayList();
        List updata = new CopyOnWriteArrayList();

        getSQL(baseList, insert, updata);
        return 0;
    }

    public int getSQL(List< BaseVO > baseList, List insert, List updata) throws Exception
    {
        baseList.parallelStream().forEach(baaeEntity ->
                                          {
                                              try
                                              {
                                                  String pk =
                                                          StringUtil.toString(baaeEntity.get(baaeEntity.getPrimaryKey()));
                                                  Map< String,Object > beanMap = transBean2Map(baaeEntity);
                                                  if (PubEnvUtil.isEmpty(pk))
                                                  {
                                                      beanMap.put(baaeEntity.getPrimaryKey(), UUID.randomUUID());
                                                      insert.add(SqlHelper.getInsertSQL(baaeEntity.getTableName(),
                                                                                        beanMap));
                                                  }
                                                  else
                                                  {
                                                      updata.add(SqlHelper.getUpdateByIdSQL(baaeEntity.getTableName()
                                                              , beanMap, baaeEntity.getPrimaryKey()));
                                                  }
                                                  // 处理子表
                                                  if (PubEnvUtil.getSize(baaeEntity.getChildInfo()) > 0)
                                                  {
                                                      Map< String,List > childInfo = baaeEntity.getChildInfo();
                                                      for (String table : childInfo.keySet())
                                                      { getSQL(childInfo.get(table), insert, updata); }
                                                  }
                                              }
                                              catch (Exception e)
                                              {
                                                  throw new RuntimeException(e);
                                              }

                                          });

        return 0;
    }


    public Map< String,Object > transBean2Map(Object obj) throws Exception
    {
        if (null == obj) { return null; }
        Map< String,Object > map = new LinkedHashMap< String,Object >();
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors)
        {
            String key = property.getName();
            // 过滤class属性
            if (!(key.equals("class") || key.equals("Class")))
            {
                Method getter = property.getReadMethod();
                if (null != getter)
                {
                    Object value = getter.invoke(obj);
                    map.put(key, value);
                }
            }
        }
        return map;
    }

    public List< BaseVO > getBeanAggEntity(String targZlass, List< Map< String,Object > > jsonObj) throws Exception
    {
        BaseVO parentVO = ReflectionUtil.newInstance(targZlass);
        List< BaseVO > fatherList = new ArrayList<>(PubEnvUtil.getSize(jsonObj));
        jsonObj.stream().forEach(temp ->
                                 {
                                     fatherList.add(getBhelp().parseVO(temp, parentVO.getClass()));
                                 });

        handlChildEntity(parentVO, fatherList, Boolean.FALSE);
        System.out.println(JsonUtil.transObj2Josn(fatherList));

        return fatherList;
    }

    public List< BaseVO > getBeanAggEntity(String targZlass, String selectSQL) throws Exception
    {
        BaseVO pVO = ReflectionUtil.newInstance(targZlass);
        List< Map< String,Object > > objList = getXbaseMapper().getObjList(selectSQL);
        List< BaseVO > fatherList = new ArrayList<>(PubEnvUtil.getSize(objList));
        objList.stream().forEach(temp ->
                                 {
                                     fatherList.add(getBhelp().parseVO(temp, pVO.getClass()));
                                 });

        handlChildEntity(pVO, fatherList, Boolean.TRUE);
        System.out.println(JsonUtil.transObj2Josn(fatherList));

        return fatherList;
    }

    /************************************************************* <br>
     *
     *说明: 处理子表<br>
     *
     * @Description   <br>
     * @Date Created on 2022/8/17  11:30  <br>
     * @param parentEntity
     * @param parents
     * @return void  <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    private void handlChildEntity(BaseVO parentEntity, List< BaseVO > parents, Boolean isEmptyChild)
    {
        for (String childcode : parentEntity.getChildInfo().keySet())
        {
            BaseVO cVO = ReflectionUtil.newInstance(parentEntity.getChildEntity().get(childcode));
            List list = new ArrayList();
            // 
            if (isEmptyChild)
            {
                String selectSQL = SqlHelper.getSelectSQL(childcode, null
                        , parentEntity.getPrimaryKey() + " IN (" + SqlHelper.getPkInSQL(parents,
                                                                                        parentEntity.getPrimaryKey()) +
                                                                  ")", "");
                List< Map< String,Object > > childList = getXbaseMapper().getObjList(selectSQL);

                if (PubEnvUtil.getSize(childList) > 0)
                {
                    Map< String,List > childMap =
                            receiveGroupListMap(childList, parentEntity.getPrimaryKey(), cVO.getClass());
                    for (BaseVO pEntity : parents)
                    { pEntity.getChildInfo().put(childcode, childMap.get(pEntity.get(pEntity.getPrimaryKey()))); }
                    list = childList;
                }
            }
            else
            {
                for (BaseVO pEntity : parents)
                {
                    if (PubEnvUtil.getSize(pEntity.getChildInfo().get(childcode)) > 0)
                    {
                        Map< String,List > childMap =
                                receiveGroupListMap(pEntity.getChildInfo().get(childcode),
                                                    parentEntity.getPrimaryKey(),
                                                    cVO.getClass());
                        pEntity.getChildInfo().put(childcode, childMap.get(pEntity.get(pEntity.getPrimaryKey())));
                        list = pEntity.getChildInfo().get(childcode);
                    }
                }
            }
            // 寻找孙表
            handlChildEntity(cVO, list, isEmptyChild);
        }
    }

    /************************************************************* <br>
     *
     *说明: 分组得到想要的数据<br>
     *
     * @Description   <br>
     * @Date Created on 2022/6/16  18:02  <br>
     * @param dataList
     * @param key 以谁分组<br>
     * @return java.util.Map<java.lang.String, java.util.List < java.util.Map < java.lang.String, java.lang.String>>>
     *     <br>
     * @Author LIBENCHENG
     * @version 1.0 <br>
     ************************************************************* <br>
     */
    public Map< String,List > receiveGroupListMap(List< Map< String,Object > > dataList, Object key, Class zlass)
    {
        return new ConcurrentHashMap< String,List >()
        {
            {
                for (Map temp : dataList)
                {
                    Object obj = temp.get(StringUtil.toString(key));
                    if (PubEnvUtil.isNotEmptyObj(zlass))
                    { obj = getBhelp().parseVO(temp, zlass); }
                    List< Object > tempList = get(temp.get(StringUtil.toString(key)));
                    if (PubEnvUtil.getSize(tempList) < 1)
                    { tempList = new LinkedList< Object >(); }
                    tempList.add(obj);
                    put(StringUtil.valueOfZero(temp.get(StringUtil.toString(key))), tempList);
                }
            }
        };

    }

    /**
     * 获取分页对象
     *
     * @param query 分页参数
     */
    protected IPage< T > getPage(Query query)
    {
        Page< T > page = new Page<>(query.getPage(), query.getLimit());
        page.addOrder(OrderItem.desc("table_name"));
        return page;
    }

    protected QueryWrapper< T > getWrapper(Query query)
    {
        QueryWrapper< T > wrapper = new QueryWrapper<>();
        wrapper.like(StringUtils.isNotEmpty(query.getCode()), "code", query.getCode());
        wrapper.like(StringUtils.isNotEmpty(query.getTableName()), "table_name", query.getTableName());
        wrapper.like(StringUtils.isNotEmpty(query.getAttrType()), "attr_type", query.getAttrType());
        wrapper.like(StringUtils.isNotEmpty(query.getColumnType()), "column_type", query.getColumnType());
        wrapper.like(StringUtils.isNotEmpty(query.getConnName()), "conn_name", query.getConnName());
        wrapper.eq(StringUtils.isNotEmpty(query.getDbType()), "db_type", query.getDbType());
        return wrapper;
    }

    @Override public T selectById(Serializable id)
    { return getBaseMapper().selectById(id); }

    @Override public List< T > selectBatchIds(Collection< ? extends Serializable > idList)
    { return getBaseMapper().selectBatchIds(idList); }

    @Override public List< T > selectByMap(Map< String,Object > columnMap)
    { return getBaseMapper().selectByMap(columnMap); }

    @Override public Long selectCount(Wrapper< T > queryWrapper)
    { return com.baomidou.mybatisplus.extension.toolkit.SqlHelper.retCount(getBaseMapper().selectCount(queryWrapper)); }

    @Override public List< T > selectList(Wrapper< T > queryWrapper)
    { return getBaseMapper().selectList(queryWrapper); }

    @Override public List< Map< String,Object > > selectMaps(Wrapper< T > queryWrapper)
    { return getBaseMapper().selectMaps(queryWrapper); }

    @Override public List< Object > selectObjs(Wrapper< T > queryWrapper, Function< ? super Object,T > mapper)
    {
        return getBaseMapper().selectObjs(queryWrapper)
                              .stream()
                              .filter(Objects :: nonNull)
                              .map(mapper)
                              .collect(Collectors.toList());
    }


    public class XiBeanHelper extends BeanHelper
    {
        Map< String,Map< String,ReferEntity > > referMap = new WeakHashMap<>();

        public XiBeanHelper( ) { }

        public ReferEntity parseRefer(IBaseVO bean, String propertyName, Object value)
        {
            if (PubEnvUtil.isNullObj(referMap.get(propertyName)))
            {
                Map< String,Object > refMap = getBaseMapper().getField(bean.getTableName(), propertyName);
                if (PubEnvUtil.isNullObj(refMap))
                { return new ReferEntity(); }
                ReferEntity referEntity = parseVO(refMap.get("reference"), ReferEntity.class);
                List< Map< String,Object > > refList = getBaseMapper().getObjList(referEntity.getRefSql());
                Map< String,ReferEntity > refer = receiveGroupMap(refList, referEntity);
                referMap.put(propertyName, refer);
            }

            return (ReferEntity) referMap.get(propertyName).get(value);
        }

        public Map< String,ReferEntity > receiveGroupMap(List< Map< String,Object > > dataList, ReferEntity bean)
        {
            return new LinkedHashMap< String,ReferEntity >()
            {{
                dataList.stream().forEach(beanMap ->
                                          {
                                              ReferEntity beanTemp = (ReferEntity) CollectionUtil.deepCloneObj(bean);
                                              beanTemp.setId(StringUtil.toString(beanMap.get(bean.getPkName())));
                                              beanTemp.setCode(StringUtil.toString(beanMap.get("code")));
                                              beanTemp.setName(StringUtil.toString(beanMap.get("name")));
                                              beanTemp.setData(beanMap);

                                              put(beanTemp.getId(), beanTemp);
                                          });
            }};
        }

        /**
         * 把JSON、MAP转换为VO 功能： JSON串转List 数组转List*********************************************************** <br>
         * 说明： <br>
         *
         * @param <T>
         * @param oragin
         * @param target
         * @return <br>
         * @T <br>
         * @methods pers.bc.utils.Bean.BeanUtil#parseVO <br>
         * @author licheng <br>
         * @date Created on 2020年4月3日 <br>
         * @time 下午4:54:37 <br>
         * @version 1.0 <br>
         * ************************************************************          <br>
         * @see <br>
         */
        public < T > T parseVO(Object oragin, Class< T > target)
        {
            if (oragin instanceof Map)
            { // Map转换成对象
                IBaseVO newVO = (IBaseVO) ReflectionUtil.newInstance(target);
                Map temp = (Map) oragin;
                for (Object key : temp.keySet())
                {
                    Object value = temp.get(key);
                    if (null != value)
                    { setProperty(newVO, String.valueOf(key), value); }

                }
                return (T) newVO;
            }
            //else if(PubEnvUtil.equals( ReferEntity.class,target.getClass())){}
            else if (oragin instanceof String)
            {
                // Json转成Map，再转成对象
                // Map temp = new Gson().fromJson(oragin.toString(), LinkedHashMap.class);
                return parseVO(JsonUtil.toMap(StringUtil.toString(oragin)), target);
            }
            return null;
        }

        /**
         * *********************************************************** <br>
         * *说明：设置对象属性 <br>
         *
         * @param bean
         * @param propertyName
         * @param value        <br>
         * @void <br>
         * @methods pers.bc.utils.Bean.BeanHelper#setProperty <br>
         * @author LiBencheng <br>
         * @date Created on 2021年6月21日 <br>
         * @time 下午7:39:36 <br>
         * @version 1.0 <br>
         * ************************************************************          <br>
         * @see <br>
         */
        public void setProperty(IBaseVO bean, String propertyName, Object value)
        {
            String pptType = null;
            try
            {
                Method method = getMethod(bean.getClass(), propertyName, true);
                if (propertyName != null && method == null)
                { return; }
                else if (method == null)
                { return; }
                String vType = value.getClass().getName();
                pptType = method.getParameterTypes()[0].getName();

                // add
                if (PubEnvUtil.equals(pptType, ReferEntity.class.getName()))
                { value = parseRefer(bean, propertyName, value); }

                else if (PubEnvUtil.equals(pptType, Map.class.getName()))
                {
                    // if (!(propertyName.contains("childEntity") || propertyName.contains("childInfo")))
                    // { value = BeanUtil.parseVO(value, ReferEntity.class); }
                    // else
                    if (PubEnvUtil.isEmptyObj(value)
                            && (propertyName.contains("childEntity") || propertyName.contains("childInfo")))
                    { return; }
                    else { }
                }
                // end

                else if (PubEnvUtil.equals(pptType, List.class.getName()))
                { value = ListUtil.toList(value); }
                else if (PubEnvUtil.equals(pptType, BigDecimal.class.getName()))
                { value = new BigDecimal(StringUtil.valueOfZero(value)); }
                else if (PubEnvUtil.equals(pptType, String.class.getName()))
                { value = StringUtil.valueOf(value); }
                // short：16位，最大数据存储量是65536，数据范围是-32768~32767之间。
                else if (PubEnvUtil.equals(pptType, Short.class.getName()))
                { value = Short.valueOf(StringUtil.valueOf(value)); }
                // int：32位，最大数据存储容量是2的32次方减1，数据范围是负的2的31次方到正的2的31次方减1。
                else if (PubEnvUtil.equals(pptType, Integer.class.getName()))
                { value = Integer.valueOf(StringUtil.valueOf(value)); }
                // long：64位，最大数据存储容量是2的64次方减1，数据范围为负的2的63次方到正的2的63次方减1。
                else if (PubEnvUtil.equals(pptType, Long.class.getName()))
                { value = Long.valueOf(StringUtil.valueOf(value)); }
                // double：64位，数据范围在4.9e-324~1.8e308，赋值时可以加d或D也可以不加。
                else if (PubEnvUtil.equals(pptType, Double.class.getName()))
                { value = Double.valueOf(StringUtil.valueOf(value)); }
                // float：32位，数据范围在3.4e-45~1.4e38，直接赋值时必须在数字后加上f或F。
                else if (PubEnvUtil.equals(pptType, Float.class.getName()))
                { value = Float.valueOf(StringUtil.valueOf(value)); }
                // boolean：只有true和false两个取值 例：double a =0.1f;(正确) float e = 0.1d;(错)
                else if (PubEnvUtil.equals(pptType, Boolean.class.getName()))
                { value = Boolean.valueOf(StringUtil.valueOf(value)); }
                // char：16位，存储Unicode码，用单引号赋值。
                else if (PubEnvUtil.equals(pptType, char.class.getName()))
                { value = StringUtil.valueOfEmpty(value).toCharArray()[0]; }
                // char：16位，存储Unicode码，用单引号赋值。
                else if (PubEnvUtil.equals(pptType, byte.class.getName()))
                { value = StringUtil.valueOfEmpty(value).getBytes(); }


                method.invoke(bean, value);

            }
            catch (IllegalArgumentException e)
            {
                String errStr = "Failed to set property: " + propertyName + " at bean: " + bean.getClass()
                                                                                               .getName() + " the " +
                        "property type is "
                        + pptType + ", however obtain value:" + value + " type:" + (value == null ? "null" :
                        value.getClass()
                             .getName());
                // Logger.error(errStr, e);
                throw new IllegalArgumentException(errStr, e);
            }
            catch (Exception e)
            {
                String errStr = "Failed to set property: " + propertyName
                        + " at bean: " + bean.getClass().getName() + " with " + "value:" + value;
                // Logger.error(errStr, e);
                throw new RuntimeException(errStr, e);
            }
        }
    }
}

 
